package ink.carnation.imtalk.service.impl;

import cn.hutool.core.util.HashUtil;
import com.google.common.hash.BloomFilter;
import ink.carnation.imtalk.exception.AuthorizationException;
import ink.carnation.imtalk.service.UserService;
import ink.carnation.imtalk.mapper.FriendMapper;
import ink.carnation.imtalk.mapper.UserMapper;
import ink.carnation.imtalk.model.po.FriendPo;
import ink.carnation.imtalk.model.po.UserPo;
import ink.carnation.imtalk.util.JwtUtil;
import ink.carnation.imtalk.util.TokenCacheKey;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class UserServiceImpl implements UserService {
    @Resource
    private UserMapper userMapper;
    @Resource
    private FriendMapper friendMapper;
    @Autowired
    private JwtUtil jwtUtil;
    @Autowired
    private RedisTemplate redisTemplate;
    @Resource
    private BloomFilter<String> bloomFilter;

    @Override
    public UserPo getUserById(Integer userId) {
        return userMapper.selectByPrimaryKey(userId);
    }

    @Override
    @Transactional
    public int insertUser(UserPo userPo) {
        int i = userMapper.insertSelective(userPo);
        bloomFilter.put(String.valueOf(userPo.getId()));
        return i;
    }

    @Override
    public UserPo getUserByEmail(String email) {
        if (email == null) {
            return null;
        }
        UserPo build = UserPo.builder()
                .email(email)
                .valid(true)
                .build();
        return userMapper.selectOne(build);
    }

    /**
     * 将token存入Redis中并设置token过期时间为半小时
     *
     * @param response
     * @param userId
     */
    @Override
    @Transactional
    public void saveTokenToRedis(HttpServletResponse response, Integer userId) {
        String token = jwtUtil.sign(userId, System.currentTimeMillis());
        response.setHeader("Authorization", token);
        BoundValueOperations boundValueOperations = redisTemplate.boundValueOps(TokenCacheKey.getUserTokenKey(userId));
        boundValueOperations.set(token, 30, TimeUnit.MINUTES);
    }

    /**
     * 注销，判断header和Redis中是否都存在token，然后删除Redis中的token
     *
     * @param request
     * @return
     */
    @Override
    @Transactional
    public Boolean layout(HttpServletRequest request) {
        String token = request.getHeader("Authorization");
        String userTokenKey = TokenCacheKey.getUserTokenKey(JwtUtil.getUserId(token));
        String tokenRedis = (String) redisTemplate.boundValueOps(userTokenKey).get();
        if (StringUtils.isEmpty(token) || StringUtils.isEmpty(tokenRedis)) {
            throw new AuthorizationException("token不存在,请重新登录");
        }
        return redisTemplate.delete(userTokenKey);
    }

    @Override
    @Transactional
    public Boolean isRedisTokenExist(HttpServletResponse response, Integer userId) {
        BoundValueOperations boundValueOperations = redisTemplate.boundValueOps(TokenCacheKey.getUserTokenKey(userId));
        String token = (String) boundValueOperations.get();
        if (StringUtils.isEmpty(token)) {
            return false;
        }
        response.setHeader("Authorization", token);
        boundValueOperations.expire(30, TimeUnit.MINUTES);
        return true;
    }

    @Override
    @Transactional
    public Boolean resetPassword(String email, String newPassword) {
        UserPo build = UserPo.builder()
                .email(email)
                .build();
        UserPo user = userMapper.selectOne(build);
        if (user == null) {
            return false;
        }
        user.setPassword(String.valueOf(HashUtil.rsHash(newPassword)));
        return userMapper.updateByPrimaryKeySelective(user) > 0;
    }

    @Override
    public Boolean isBloomFilterExist(Integer userId) {
        return bloomFilter.mightContain(String.valueOf(userId));
    }
}
